#ifndef UnstructMG_PAR_CSR_COMM_PKG_HPP
#define UnstructMG_PAR_CSR_COMM_PKG_HPP

#include "common.hpp"
#include "seq_mv.hpp"
#include "_hypre_parcsr_mv.h"

template<typename idx_t>
class par_CSRCommPkg
{
public:
    MPI_Comm comm = MPI_COMM_NULL;

    idx_t num_sends = 0;
    int * send_pids = nullptr;
    // 长度num_sends+1, send_map_starts[p]标记了发送给进程send_pids[p]的数据在send_buf中的起始位置
    // 而send_map_starts[p+1]-send_map_starts[p]则是发送的数据个数
    idx_t * send_map_starts = nullptr;
    // 长度send_map_starts[num_sends], send_map_elmts[send_map_starts[p]] ~ send_map_elmts[send_map_starts[p+1]] (左闭右开)
    // 记录了发送给进程send_pids[p]的数据对应本进程负责区域的局部序号
    idx_t * send_map_elmts  = nullptr;

    idx_t num_recvs = 0;
    int * recv_pids = nullptr;
    // 长度num_recvs+1，接收缓冲区长度为recv_vec_starts[num_recvs]
    // 其中recv_buf[recv_vec_starts[p]]标记了从进程recv_pids[p]接收回来的数据的起始位置
    // 而recv_vec_starts[p+1]-recv_vec_starts[p]是从进程recv_pids[p]接收回来的数据个数
    idx_t * recv_vec_starts = nullptr;
    
    MPI_Request * send_reqs = nullptr;
    MPI_Request * recv_reqs = nullptr;

    par_CSRCommPkg();
    ~par_CSRCommPkg();

    void copy_hypre(const hypre_ParCSRCommPkg hypre_pkg);
    // void copy_hypre(const void * hypre_ptr);
    
    void prepare(const idx_t num_needs, const idx_t * glb_ids, const idx_t * partitions);

    void init_update_halo(const int dof, idx_t * send_buf, idx_t * recv_vec) const;
    void init_update_halo(const int dof, const idx_t * loc_vec, idx_t * send_buf, idx_t * recv_vec) const;
    void init_update_halo(const int dof, const float * loc_vec, float * send_buf, float * recv_vec) const;
    void init_update_halo(const int dof, const double* loc_vec, double* send_buf, double* recv_vec) const;
    void wait_update_halo() const;
};
typedef par_CSRCommPkg<      int> par_CSRCommPkg_I32;
typedef par_CSRCommPkg<long long> par_CSRCommPkg_I64;

/*
template<typename idx_t, typename data_t, typename calc_t, int dof>
par_CSRCommPkg<idx_t> * transpose_commpkg(const CSRMatrix<idx_t, data_t, calc_t, dof> & offd_trans, 
    const par_CSRCommPkg<idx_t> * commpkg);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F32_DOF1 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F32_DOF2 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F32_DOF3 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F32_DOF4 &, const par_CSRCommPkg_I32*);

extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F64_DOF1 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F64_DOF2 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F64_DOF3 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F32F64_DOF4 &, const par_CSRCommPkg_I32*);

extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F64F64_DOF1 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F64F64_DOF2 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F64F64_DOF3 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F64F64_DOF4 &, const par_CSRCommPkg_I32*);
#ifdef USE_FP16
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F32_DOF1 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F32_DOF2 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F32_DOF3 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F32_DOF4 &, const par_CSRCommPkg_I32*);

extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F64_DOF1 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F64_DOF2 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F64_DOF3 &, const par_CSRCommPkg_I32*);
extern template par_CSRCommPkg_I32* transpose_commpkg(const CSRMatrix_I32F16F64_DOF4 &, const par_CSRCommPkg_I32*);
#endif
*/
#endif